import 'dart:async';
import 'dart:developer';
import 'dart:io';

import 'package:simple_server/simple_server.dart';

Future<void> main() async {
  // Create a new SimpleServer instance
  final simpleServer = SimpleServer<Context>();

  // The order of 'use' is important, they will be executed in the order they are added
  // So, we need to add some middleware first, then add routes

  // Add our implemented middleware
  // Now, after each request is completed, the number of requests for that path will be printed
  simpleServer.use(CountPathMiddleware());

  // Use CrossOriginMiddleware to handle cross-origin requests
  // This allows clients from different domains to access our server
  simpleServer.use(CrossOriginMiddleware());

  // Create and configure BodyParserMiddleware
  // This middleware parses the request body and stores the result in the context
  final bodyParserMiddleware = BodyParserMiddleware();
  // Ignore body parsing for /favicon.ico requests, as these requests usually don't contain useful data
  bodyParserMiddleware.ignoreParseBody('/favicon.ico');
  simpleServer.use(bodyParserMiddleware);

  // Create a Map to store the number of requests for each IP address
  final ipCountMap = <String, int>{};

  // Create and configure LoggingMiddleware
  final loggingMiddleware = LoggingMiddleware();
  // Add a listener to count requests for each IP address
  loggingMiddleware.addClientIpListener((_, ip) {
    ipCountMap[ip] = (ipCountMap[ip] ?? 0) + 1;
  });
  simpleServer.use(loggingMiddleware);

  // When the query parameter contains 'ok=ok', the request is considered authenticated
  Future<void> okCheckAuth(token, context, next) async {
    if (context['ok'] != 'ok') return;
    context.auth = true;
    await next();
  }

  // Create and configure AuthMiddleware
  final authMiddleware = AuthMiddleware();
  // Add authentication check for '/hello' path
  authMiddleware.addCheckAuth('/hello', okCheckAuth);
  simpleServer.use(authMiddleware);

  // Create and configure MappingMiddleware
  // This middleware is used to define routes and handler functions
  final mappingMiddleware = MappingMiddleware();

  // Define the handler function for '/hello' route
  mappingMiddleware.get(
    '/hello',
    handler: (c) async {
      // Check authentication status, actually this is unnecessary as mappingMiddleware will check automatically
      // if (!c.auth) {
      //   c.unauthorized('Authentication required');
      //   return;
      // }
      final name = c['name'];
      if (name == null) {
        c.badRequest('name is required');
        return;
      }
      c.ok();
      c.write('hello, $name');
    },
    // Equivalent to `authMiddleware.addCheckAuth('/hello', okCheckAuth)`
    // auth: okCheckAuth,
  );

  // Define the handler function for '/favicon.ico' route
  // Here we simply return 404 Not Found
  mappingMiddleware.get(
    '/favicon.ico',
    handler: (c) async => c.statusCode = HttpStatus.notFound,
    // Equivalent to `bodyParserMiddleware.ignoreParseBody('/favicon.ico')`
    // ignoreBodyParser: true,
  );

  simpleServer.use(mappingMiddleware);

  // Create and start the HTTP server
  final httpServer = await HttpServer.bind('localhost', 0);
  final port = httpServer.port;
  httpServer.listen((req) => simpleServer.handleRequest(Context(req)));
  log('Server started: http://127.0.0.1:$port');

  // Wait for exit signal (input 'exit' or press Ctrl+C)
  await simpleServer.waitExit(httpServer.close);
}

// Middleware for counting path access
class CountPathMiddleware extends Middleware<Context> {
  final Map<String, int> _pathCountMap = {};

  @override
  Future<void> call(Context context, Next next) async {
    final path = context.path;
    final newCount = (_pathCountMap[path] ?? 0) + 1;
    _pathCountMap[path] = newCount;
    await next();
    print('$path: $newCount');
  }
}